/**
 * Copyright (c) 2006-2015 Hzins Ltd. All Rights Reserved.
 * <p>
 * This code is the confidential and proprietary information of
 * Hzins. You shall not disclose such Confidential Information
 * and shall use it only in accordance with the terms of the agreements
 * you entered into with Hzins,http://www.hzins.com.
 */
package com.huize.ladon.apm.common.util;


import java.io.UTFDataFormatException;
import java.io.UnsupportedEncodingException;

/**
 * byte字节码 截取工具类
 *
 * @author hz15051315
 * @date 2019年2月28日 上午9:44:19
 */
public class SubtractTools {

    private static final int DEFAULT_SHORT_LENGTH = 2;

    private static final int DEFAULT_INT_LENGTH = 4;

    private static final int MAX_UTF_LEN = 65535;

    private byte[] buf = null;

    private int pos = 0;

    public SubtractTools(byte[] buf, int offset) {
        this.buf = buf;
        this.pos = offset;
    }

    public void reset(byte[] buf, int offset) {
        this.buf = buf;
        this.pos = offset;
    }

    /**
     * 获取1个byte值
     *
     * @return
     */
    public byte getByte() {
        byte ret = buf[pos];
        pos++;
        return ret;
    }

    /**
     * 获取1个short值
     *
     * @return
     */
    public short getShort() {
        byte[] bys = getBytes(DEFAULT_SHORT_LENGTH);
        short ret = ByteUtils.bytesToShort(bys);
        return ret;
    }

    /**
     * 获取1个int值
     *
     * @return
     */
    public int getInt() {
        byte[] bys = getBytes(DEFAULT_INT_LENGTH);
        int ret = ByteUtils.bytesToInt(bys);
        return ret;
    }

    /**
     * 获取len长度的byte[]值
     *
     * @return
     */
    public byte[] getBytes(int len) {
        if (len <= 0) {
            return null;
        }
        byte[] ret = new byte[len];
        System.arraycopy(buf, pos, ret, 0, len);
        pos += len;
        return ret;
    }

    /**
     * 从缓冲二进制数据队列中的当前位置，获得一个以二进制0为结束符的字符串
     *
     * @return 根据byte数组生成的字符串(缺省编码)
     */
    public String getCString() {
        String ret = null;
        int i = pos;
        int len = buf.length;
        for (i = pos; i < len; i++) {
            if (buf[i] == 0) {
                break;
            }
        }
        if (i == len) {
            return null;
        }

        len = i - pos;
        pos += len + 1;
        ret = new String(buf, pos - len - 1, len);
        return ret;
    }

    /**
     * 从缓冲二进制数据队列中的当前位置获得一个以二进制0为结束符的字符串
     *
     * @param charset 字符串编码
     * @return 根据byte数组生成的字符串(缺省编码)
     * @throws UnsupportedEncodingException 以charset编码生成String失败则抛出此异常
     */
    public String getCString(String charset) throws UnsupportedEncodingException {
        int i = pos;
        int len = buf.length;
        for (i = pos; i < len; i++) {
            if (buf[i] == 0) {
                break;
            }
        }
        if (i == len) {
            return null;
        }

        len = i - pos;
        pos += len + 1;
        String ret = new String(buf, pos - len - 1, len, charset);
        return ret;
    }

    /**
     * 从缓冲二进制数据队列中的当前位置获得len个字符串
     *
     * @param len 从当前位置转换成字符串的二进制byte长度
     * @return 根据byte数组生成的字符串(缺省编码)
     */
    public String getString(int len) {
        if (len <= 0) {
            return null;
        }
        pos += len;
        String ret = new String(buf, pos - len, len);
        return ret.trim();
    }

    /**
     * 从缓冲二进制数据队列中的当前位置获得len个字符串
     *
     * @param len     从当前位置转换成字符串的二进制byte长度
     * @param charset 编码格式
     * @return 根据byte数组生成的字符串
     * @throws UnsupportedEncodingException
     */
    public String getString(int len, String charset) throws UnsupportedEncodingException {
        if (len <= 0) {
            return null;
        }
        pos += len;
        String ret = new String(buf, pos - len, len, charset);
        return ret;
    }

    /**
     * 从缓冲二进制数据队列中的当前位置获得一个UTF-8字符串
     *
     * @return UTF-8字符串
     * @throws UTFDataFormatException 如果获取的字节数组不是一个有效的UTF-8字符串则抛出此异常.
     */
    public String getUTF() throws UTFDataFormatException {
        int utfLen = getShort();
        if (utfLen < 0) {
            utfLen += MAX_UTF_LEN;
        }

        if (utfLen > MAX_UTF_LEN) {
            throw new UTFDataFormatException(String.format("UTF-8字符串长度太长(%d)", utfLen));
        }

        byte[] buffers = getBytes(utfLen);
        StringBuffer result = new StringBuffer(utfLen);
        int c = 0;
        int char2 = 0;
        int char3 = 0;
        int count = 0;

        String errMsg = null;
        while (count < utfLen) {
            c = (int) buffers[count] & 0xff;
            switch (c >> 4) {
                case 0:
                case 1:
                case 2:
                case 3:
                case 4:
                case 5:
                case 6:
                case 7:

                    /* 0xxxxxxx */
                    count++;
                    result.append((char) c);
                    break;
                case 12:
                case 13:

                    /* 110x xxxx 10xx xxxx */
                    count += 2;
                    if (count > utfLen) {
                        errMsg = String.format("13 count(%d) > utfLen(%s)", count, utfLen);
                        throw new UTFDataFormatException(errMsg);
                    }

                    char2 = (int) buffers[count - 1];
                    if ((char2 & 0xC0) != 0x80) {
                        errMsg = String.format("13 (char2(%d) & 0xC0) != 0x80", char2);
                        throw new UTFDataFormatException(errMsg);
                    }

                    result.append((char) (((c & 0x1F) << 6) | (char2 & 0x3F)));
                    break;
                case 14:

                    /* 1110 xxxx 10xx xxxx 10xx xxxx */
                    count += 3;
                    if (count > utfLen) {
                        errMsg = String.format("14 count(%d) > utfLen(%d)", count, utfLen);
                        throw new UTFDataFormatException(errMsg);
                    }

                    char2 = (int) buffers[count - 2];
                    char3 = (int) buffers[count - 1];
                    if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80)) {
                        errMsg = String.format("14 (char2(%d) & 0xC0) != 0x80 || (char3(%d) & 0xC0) != 0x80", char2, char3);
                        throw new UTFDataFormatException(errMsg);
                    }

                    result.append((char) (((c & 0x0F) << 12) | ((char2 & 0x3F) << 6) | ((char3 & 0x3F) << 0)));
                    break;
                default:

                    /* 10xx xxxx, 1111 xxxx */
                    errMsg = String.format("default(%d)", c);
                    throw new UTFDataFormatException(errMsg);
            }
        }

        // The number of chars produced may be less than utflen
        return new String(result);
    }

    /**
     * 从缓冲二进制数据队列中的当前位置获得一个字符串并去掉字符串两端空格
     *
     * @param len 从当前位置转换成字符串的二进制byte长度
     * @return 根据byte数组生成的字符串(缺省编码)
     */
    public String getTrimString(int len) {
        String ret = getString(len);
        return ret.trim();
    }

    /**
     * 从缓冲二进制数据队列中的当前位置，获得len个长度的字符串，并且字符串以二进制0为结束符，最后去掉字符串两端空格
     *
     * @param len     从当前位置转换成字符串的二进制byte长度
     * @param charset 编码格式
     * @return 根据byte数组生成的字符串(缺省编码)
     * @throws UnsupportedEncodingException
     */
    public String getTrimString(int len, String charset) throws UnsupportedEncodingException {
        String ret = getString(len, charset);
        return ret.trim();
    }

    /**
     * 从缓冲二进制数据队列中的当前位置，获得一个以二进制0为结束符的字符串，并去掉字符串两端空格
     *
     * @return 根据byte数组生成的字符串(缺省编码)
     */
    public String getTrimCString() {
        String ret = getCString();
        return ret.trim();
    }

    /**
     * 从缓冲二进制数据队列中的当前位置，获得一个以二进制0为结束符的字符串，并去掉字符串两端空格
     *
     * @param charset 字符串编码
     * @return 根据byte数组生成的字符串(缺省编码)
     * @throws UnsupportedEncodingException 以charset编码生成String失败则抛出此异常
     */
    public String getTrimCString(String charset) throws UnsupportedEncodingException {
        String ret = getCString(charset);
        return ret.trim();
    }

    /**
     * 从缓冲二进制数据队列中的当前位置,获得一个UTF-8字符串,并去掉字符串两端空格
     *
     * @return UTF-8字符串
     * @throws UTFDataFormatException 如果获取的字节数组不是一个有效的UTF-8字符串则抛出此异常.
     */
    public String getTrimUTF() throws UTFDataFormatException {
        String ret = getUTF();
        return ret.trim();
    }

    /**
     * 获得当前数据的长度
     *
     * @return int
     */
    public int size() {
        return pos;
    }

    public void resetPos(int _pos) {
        this.pos = _pos;
    }

}
